/***************************************************************************
 *                                                                         *
 * Copyright (c) 2007 - 2009 Nuvoton Technology Corp. All rights reserved.*
 *                                                                         *
 ***************************************************************************/
 
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "drv_api.h"
#include "wbio.h"
#else
#include "wblib.h"
#endif

#include "NUC900_VPOST_Regs.h"
#include "NUC900_VPOST.h"
#ifdef	HAVE_AUO_A035QN02

#ifdef ECOS
cyg_handle_t	vpost_int_handle;
cyg_interrupt	vpost_int_holder;
#endif

static BOOL g_powerup=FALSE;
static UINT32 g_nScreenWidth;
static UINT32 g_nScreenHeight;

static void Delay(int nCnt)
{
	volatile int  loop;
	for (loop=0; loop<nCnt; loop++);
}

#if 1
static VOID vpostDisp_F_ISR(void)
{
    outpw(REG_LCM_INT_CS,inpw(REG_LCM_INT_CS) | VPOSTB_DISP_F_STATUS);
    vpostVAStartTrigger();
}
static VOID vpostUNDERRUN_ISR(void)
{
	outpw(REG_LCM_INT_CS,inpw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_INT);
}

static VOID vpostBUS_ERROR_ISR(void)
{
	outpw(REG_LCM_INT_CS,inpw(REG_LCM_INT_CS) | VPOSTB_BUS_ERROR_INT);
}
#ifdef ECOS
static cyg_uint32 vpostIntHandler(cyg_vector_t vector, cyg_addrword_t data)
#else
static VOID vpostIntHandler(void)
#endif
{
   /* clear VPOST interrupt state */
   UINT32 uintstatus;
   
   uintstatus = inpw(REG_LCM_INT_CS);
   if (uintstatus & VPOSTB_DISP_F_STATUS)  
      vpostDisp_F_ISR();
   else if (uintstatus & VPOSTB_UNDERRUN_INT)
      vpostUNDERRUN_ISR();
   else if (uintstatus & VPOSTB_BUS_ERROR_INT)
      vpostBUS_ERROR_ISR();

#ifdef ECOS
	return CYG_ISR_HANDLED;
#endif
}

static void vpostEnable_Int(void)
{
#ifdef ECOS
    cyg_interrupt_create(IRQ_LCD, 1, 0, vpostIntHandler, NULL, &vpost_int_handle, &vpost_int_holder);
    cyg_interrupt_attach(vpost_int_handle);
    cyg_interrupt_unmask(IRQ_LCD);
#else
    sysInstallISR(IRQ_LEVEL_1, IRQ_LCD, (PVOID)vpostIntHandler);
    /* enable VPOST interrupt */
    sysEnableInterrupt(IRQ_LCD);
#endif
    writew(REG_LCM_DCCS,readw(REG_LCM_DCCS) | VPOSTB_DISP_INT_EN);
    writew(REG_LCM_INT_CS,readw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_EN | VPOSTB_DISP_F_EN);
}
#endif

static void init_gpio(void)
{
 	//set share pin to GPIOG2, GPIOG3, GPIOE13
 	outpw(REG_MFSEL, inpw(REG_MFSEL) & 0xFFFCC7FF);
 
 	//set gpio dir
 	outpw(REG_GPIOG_DIR, 0xC);         // 950's gpioG [2,3]
	outpw(REG_GPIOE_DIR, 0x2000);   // 950's gpioE[13]
}

#define CS_HIGH  do { outpw(REG_GPIOE_DATAOUT, 1<<13); }while(0);
#define CS_LOW   do { outpw(REG_GPIOE_DATAOUT, inpw(REG_GPIOE_DATAOUT) & 0x1FFF); }while(0);
#define SCL_HIGH do { outpw(REG_GPIOG_DATAOUT, inpw(REG_GPIOG_DATAOUT) | 1<<2); }while(0);
#define SCL_LOW  do { outpw(REG_GPIOG_DATAOUT, inpw(REG_GPIOG_DATAOUT) & 0x1FFFB); }while(0);
#define SDA_HIGH do { outpw(REG_GPIOG_DATAOUT, inpw(REG_GPIOG_DATAOUT) | 1<<3); }while(0);
#define SDA_LOW  do { outpw(REG_GPIOG_DATAOUT, inpw(REG_GPIOG_DATAOUT) & 0x1FFF7); }while(0);
 

static void gpio_write_reg_data(unsigned char usRegIndex, unsigned short usRegData)
{
 	int i;
 
 	CS_HIGH
 	CS_LOW
 
 	SDA_LOW
 	Delay(0x10);
 	SCL_LOW
 	Delay(0x50);
 	SCL_HIGH
 	Delay(0x50);
 	SCL_LOW
 	Delay(0x50);
 
	for(i=7;i>=0;i--)     // command has 8 bits
	{
	  	if((usRegIndex >> i ) & 0x1)
	   		SDA_HIGH 
	  	else
	   		SDA_LOW
	  
	  	Delay(0x10);  
	  	SCL_HIGH
	  	Delay(0x50);
	  	SCL_LOW
	  	Delay(0x50);
	}
 
 	SDA_HIGH
 	Delay(0x10);
 	SCL_HIGH
 	Delay(0x50);
 	SCL_LOW
 	Delay(0x50);
	for(i=15;i>=0;i--)    // command has 16 bits
	{
		if((usRegData >> i ) & 0x1)
	   		SDA_HIGH 
	  	else
	   		SDA_LOW
	  
	  	Delay(0x10);  
	  	SCL_HIGH
	  	Delay(0x50); 
	  	if(i!=0)
	  	{
	   		SCL_LOW
	   		Delay(0x50);
		}
	}
 
 	CS_HIGH 
 	SDA_LOW
 	SCL_HIGH 
 	Delay(0x50); 
}

static Setup_AUO_A035QN02()
{
	outpw(REG_MFSEL, inpw(REG_MFSEL) & 0xFEFFFFF);
	outpw(GPIO_BA+0x54, 1<<2);

	init_gpio();

		/* Initial table */  
	gpio_write_reg_data(0x01, 0x2AEF);
	gpio_write_reg_data(0x03, 0x7872);  // recommend 0x7472
	//gpio_write_reg_data(0x03, 0x7472);
	
	gpio_write_reg_data(0x0C, 0x0003);  // recommend 0x0002
	//gpio_write_reg_data(0x0C, 0x0002);
	
	gpio_write_reg_data(0x0D, 0x000C);
	gpio_write_reg_data(0x0E, 0x3200);  // recommend 0x3100
	//gpio_write_reg_data(0x0E, 0x3100);
	
	gpio_write_reg_data(0x1E, 0x00AA);  // recommend 0x00A4
	//gpio_write_reg_data(0x1E, 0x00A4);
	
	gpio_write_reg_data(0x2E, 0xB945);
	gpio_write_reg_data(0x30, 0x0304);
	gpio_write_reg_data(0x31, 0x0507);
	gpio_write_reg_data(0x32, 0x0405);
	gpio_write_reg_data(0x33, 0x0007);
	gpio_write_reg_data(0x34, 0x0507);
	gpio_write_reg_data(0x35, 0x0004);
	gpio_write_reg_data(0x36, 0x0605);
	gpio_write_reg_data(0x37, 0x0103);
	gpio_write_reg_data(0x3A, 0x000F);
	gpio_write_reg_data(0x3B, 0x000F);

	gpio_write_reg_data(0x16, 0x9FBF);    // recommend 0x9F86
	//gpio_write_reg_data(0x16, 0x9F86);
	
	gpio_write_reg_data(0x17, 0x0012);
	//outpw(GPIO_BA+0x58, 1<<2);
	//gpio_write_reg_data(0x17, 0x0002);

}

INT vpostOSDInit_AUO_A035QN02(POSDFORMATEX pOSDformatex)
{

    if ((pOSDformatex->nXstart > g_nScreenWidth) || (pOSDformatex->nYstart>g_nScreenHeight))
        return ERR_BAD_PARAMETER;

    vpostSetOSDSrc(pOSDformatex->ucOSDSrcFormat);
    
    vpostSetOSDBuffer(pOSDformatex->pFrameBuffer);
    /*if (ucOSDSrcType != 0)
     	writew(REG_LCM_DCCS,readw(REG_LCM_DCCS) | ucOSDSrcType);
     else
     	writew(REG_LCM_DCCS,readw(REG_LCM_DCCS) & 0xffff0fff);//clear OSD SRC setting	
	*/
	vpostOSDSetWindow(pOSDformatex->nXstart,pOSDformatex->nYstart,pOSDformatex->nOSDWidth,pOSDformatex->nOSDHeight);
     
	writew(REG_LCM_OSD_FBCTRL,0);//clear OSD STRIDE,FF setting
    if ((pOSDformatex->ucOSDSrcFormat==OSD_SRC_RGB888)||(pOSDformatex->ucOSDSrcFormat==OSD_SRC_RGB666))
    {
		writew(REG_LCM_OSD_FBCTRL,(pOSDformatex->nImageWidth<<16) | pOSDformatex->nImageWidth); //OSDFF~OSD_STRIDE
    }
    else if (pOSDformatex->ucOSDSrcFormat==OSD_SRC_RGB332)
    {
    	writew(REG_LCM_OSD_FBCTRL,((pOSDformatex->nImageWidth/4)<<16) | (pOSDformatex->nImageWidth/4)); //OSDFF~OSD_STRIDE
    }
    else{
        writew(REG_LCM_OSD_FBCTRL,((pOSDformatex->nImageWidth/2)<<16) | (pOSDformatex->nImageWidth/2)); //OSDFF~OSD_STRIDE
    }
    vpostOSDScalingCtrl(1,0,0);
    vpostOSDSetOverlay(DISPLAY_OSD,DISPLAY_OSD,0,0,0);
    return 0;
}

static VOID vpostSetCRTC_AUO_A035QN02()
{   
	
	outpw(REG_LCM_CRTC_SIZE,0x00F40150); //CRTC_SIZE
    outpw(REG_LCM_CRTC_DEND,0x00F00140); //CRTC_DEND
    outpw(REG_LCM_CRTC_HR,0x01450141); //CRTC_HR
    outpw(REG_LCM_CRTC_HSYNC,0x014F014D); //CRTC_HSYNC
    outpw(REG_LCM_CRTC_VR,0x00F300F2); //CRTC_VR
    
	    

}

INT vpostLCMInit_AUO_A035QN02(PLCDFORMATEX plcdformatex)
{
	UINT32 VA_FF;
	UINT32 VA_Sride;
	UINT32 nBytesPixel;
	
	if (g_powerup)
        return 0;
	
	outpw(REG_MFSEL,inpw(REG_MFSEL) | (3<<2));
	outpw(REG_CLKEN,inpw(REG_CLKEN) | 0x1);
	outpw(0xb0000220,0x1);//SW reset LCD
	
	outpw(REG_LCM_DCCS,inpw(REG_LCM_DCCS) | VPOSTB_ENG_RST);
	Delay(100);
	outpw(REG_LCM_DCCS,inpw(REG_LCM_DCCS) & ~VPOSTB_ENG_RST);
	Delay(100);
	
	
	g_nScreenWidth = plcdformatex->nScreenWidth = 320;
	g_nScreenHeight = plcdformatex->nScreenHeight = 240;
	
	if ( (plcdformatex->ucVASrcFormat == VA_SRC_RGB888) || (plcdformatex->ucVASrcFormat == VA_SRC_RGB666))//4 BytesPixel
	{
	    nBytesPixel = 4;
	}
	else
	{
	    nBytesPixel = 2;
	}
	plcdformatex->nFrameBufferSize = 240*320*nBytesPixel;
	
	
	/* set the display buffer (fetch from VA_BADDR0, if at single buffer mode)*/
	if (vpostAllocVABuffer(plcdformatex,nBytesPixel)==FALSE)
		return ERR_NULL_BUF;
	
	/* set display mode */
	vpostSetDisplayMode(0);//continue mode
	
	/* set display video source format */
	vpostSetVASrc(plcdformatex->ucVASrcFormat);
	
	outpw(REG_LCM_DEV_CTRL,0x0);//clear register
	writew(REG_LCM_DEV_CTRL,inpw(REG_LCM_DEV_CTRL) | VPOSTB_DATA16or18
												   | VPOSTB_COLORTYPE_256K
												   | VPOSTB_DEVICE_SYNC_HIGHCOLOR
												   |(1<<19));
	
	

	vpostVAScalingCtrl(1,0,1,0,VA_SCALE_INTERPOLATION);
	vpostSetCRTC_AUO_A035QN02();
	
	/* set video stream frame buffer control */
	VA_FF = (plcdformatex->nScreenWidth)*(nBytesPixel)/4;//word unit
    VA_Sride = (plcdformatex->nScreenWidth)*(nBytesPixel)/4;//word unit
    outpw(REG_LCM_VA_FBCTRL,inpw(REG_LCM_VA_FBCTRL) &~0x7ff07ff | (VA_FF<<16) | VA_Sride);
						
    vpostVAStartTrigger();
    Setup_AUO_A035QN02();						
	//vpostEnable_Int();
	// wait first frame flushed out, and then turn on back light.
	{
		unsigned int volatile ii;
		for(ii = 0; ii < 0x400000; ii++);
		outpw(GPIO_BA+0x58, 1<<2);
	}
	
	
	if (!g_powerup)
        g_powerup=TRUE;
	return 0;
}

INT vpostLCMDeinit_AUO_A035QN02()
{
    if (!g_powerup)
        return ERR_POWER_STATE;
    g_powerup = FALSE;
    vpostFreeVABuffer();
    vpostVAStopTrigger();
    return 0;
}

#endif	/* HAVE_AUO_A035QN02 */